#ifndef S19FILE_H
#define S19FILE_H

#include <string>
#include <cstdint>
#include <vector>


/**************************************************************************
** .s19 file format
***************************************************************************
** .s19文件中, 每一行都是一个记录(record)
** | type  | count | address     | data       | checksum | end-of-line |
** | 1byte | 1byte | 4/6/8 byte  | 0-32byte   | 2byte    | 2byte       |
**
** type(记录类型): 值为'S' + '1'/'2'/'3'/'7'/'8'/'9'
**   S0: S格式文件的第一个记录, 表示文件名(含路径), 存储地址部分没有使用，以0000置位. 此记录表示记录的开始，无需下载到MCU
**   S1: 地址为2字节(4个字符)的记录
**   S2: 地址为3字节的记录
**   S3: 地址为4字节的记录
**   S4: 保留
**   S5: 标记本文件中16bit的S1、S2、S3记录的个数(非.s19文件必须). 如果记录计数 <= 0xFFFF, 则使用此记录; 否则使用S6记录
**   S6: 标记本文件中24bit的S1、S2、S3记录的个数(非.s19文件必须). 如果记录计数 <= 0xFFFFFF, 则使用此记录;
**       如果 < 0xFFFF, 则使用S5记录
**   S7: 地址为4字节，表示程序的开始执行地址，代码/数据部分没有被使用，此行表示程序的结束，无需下载到MCU
**   S8: 地址为3字节，表示程序的开始执行地址，代码/数据部分没有被使用，此行表示程序的结束，无需下载到MCU
**   S9: 地址为2字节，表示程序的开始执行地址，代码/数据部分没有被使用，此行表示程序的结束，无需下载到MCU
**
** count(记录长度): 显示在记录中剩余的字节数, 计算方法:
**                 记录长度 = 存储地址字节数 + 代码/数据字节数 + 校验和字节数
**
** address(存储地址): 2/3/4byte(由记录类型type决定), 用来表示代码/数据应该装载的起始地址
**
** data(代码/数据): 表示需要下载到MCU中的数据
**
** checksum(校验和): 校验数据, 计算方法:
**                  校验和 = 0Xff – (记录长度 + 存储地址 + 代码/数据)
**
** ref:
** https://blog.csdn.net/wteruiycbqqvwt/article/details/101029961
*/

typedef enum RecordType : uint8_t {
    RECORD_S0 = 0, // file path info, useless for MCU
    RECORD_S1,
    RECORD_S2,
    RECORD_S3,
    RECORD_S4, // reserved
    RECORD_S5,
    RECORD_S6,
    RECORD_S7,
    RECORD_S8,
    RECORD_S9
} RecordType;

struct Record
{
    RecordType  type;
    uint8_t     count;
    uint32_t    address;
    std::vector<uint8_t> data;
    uint8_t     checksum;
};


/**
 * @brief The S19File class
 */
class S19File
{
public:
    S19File(const std::string &filepath);
    bool LoadFile();
    bool CheckFile() const;

    /**
     *  for debug
     */
    void PrintRecords() const;

private:
    bool ParseLine(const std::string line, int linenum);

    std::string m_filePath;
    std::vector<Record> m_records;
    int m_errorCode;
    mutable std::string m_errorString;
};

#endif // S19FILE_H
